/*!
 * \file      spi-board.c
 *
 * \brief     Target board SPI driver implementation
 *
 * \copyright Revised BSD License, see section \ref LICENSE.
 *
 * \code
 *                ______                              _
 *               / _____)             _              | |
 *              ( (____  _____ ____ _| |_ _____  ____| |__
 *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 *               _____) ) ____| | | || |_| ____( (___| | | |
 *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
 *              (C)2013-2017 Semtech
 *
 * \endcode
 *
 * \author    Miguel Luis ( Semtech )
 *
 * \author    Gregory Cristian ( Semtech )
 */
#include <stddef.h>
#include "n32l43x_spi.h"
#include "utilities.h"
#include "board.h"
#include "gpio.h"
#include "spi-board.h"

struct SpiHandleType{
    SPI_Module* SPI;
    SPI_InitType Init;
};

static struct SpiHandleType SpiHandle[2]={0};

void SpiInit( Spi_t *obj, SpiId_t spiId, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss )
{
    CRITICAL_SECTION_BEGIN( );

    obj->SpiId = spiId;

    if( spiId == SPI_1 )
    {
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_SPI1, ENABLE);
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_SPI1, DISABLE);
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_SPI1, ENABLE);
        
        SpiHandle[spiId].SPI = SPI1;

        GpioInit( &obj->Mosi, mosi, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF0_SPI1 );
        GpioInit( &obj->Miso, miso, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF0_SPI1 );
        GpioInit( &obj->Sclk, sclk, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF0_SPI1 );
        GpioInit( &obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF0_SPI1 );
    }
    else
    {
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_SPI2, ENABLE);
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_SPI2, DISABLE);
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_SPI2, ENABLE);
        
        SpiHandle[spiId].SPI = SPI2;

        GpioInit( &obj->Mosi, mosi, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI2 );
        GpioInit( &obj->Miso, miso, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI2 );
        GpioInit( &obj->Sclk, sclk, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF5_SPI2 );
        GpioInit( &obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF5_SPI2 );
    }

    if( nss == NC )
    {
        SpiHandle[spiId].Init.NSS = SPI_NSS_SOFT;
        SpiFormat( obj, SPI_DATA_SIZE_8BITS, SPI_CLKPOL_LOW, SPI_CLKPHA_FIRST_EDGE, 0 );
    }
    else
    {
        SpiHandle[spiId].Init.NSS = SPI_NSS_HARD;
        SpiFormat( obj, SPI_DATA_SIZE_8BITS, SPI_CLKPOL_LOW, SPI_CLKPHA_FIRST_EDGE, 1 );
    }
    SpiFrequency( obj, 10000000 );

    SPI_Init( SpiHandle[spiId].SPI, &SpiHandle[spiId].Init);
    
    SPI_Enable(SpiHandle[spiId].SPI, ENABLE);

    CRITICAL_SECTION_END( );
}

void SpiDeInit( Spi_t *obj )
{
    SPI_I2S_DeInit( SpiHandle[obj->SpiId].SPI );

    GpioInit( &obj->Mosi, obj->Mosi.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &obj->Miso, obj->Miso.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_DOWN, 0 );
    GpioInit( &obj->Sclk, obj->Sclk.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &obj->Nss, obj->Nss.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 );
}

void SpiFormat( Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave )
{
    SpiHandle[obj->SpiId].Init.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    if( bits == SPI_DATA_SIZE_8BITS )
    {
        SpiHandle[obj->SpiId].Init.DataLen = SPI_DATA_SIZE_8BITS;
    }
    else
    {
        SpiHandle[obj->SpiId].Init.DataLen = SPI_DATA_SIZE_16BITS;
    }
    SpiHandle[obj->SpiId].Init.CLKPOL = cpol;
    SpiHandle[obj->SpiId].Init.CLKPHA = cpha;
    SpiHandle[obj->SpiId].Init.FirstBit = SPI_FB_MSB;
    SpiHandle[obj->SpiId].Init.CRCPoly = 7;

    if( slave == 0 )
    {
        SpiHandle[obj->SpiId].Init.SpiMode = SPI_MODE_MASTER;
    }
    else
    {
        SpiHandle[obj->SpiId].Init.SpiMode = SPI_MODE_SLAVE;
    }
}

void SpiFrequency( Spi_t *obj, uint32_t hz )
{
    uint32_t divisor = 0;
    uint32_t sysClkTmp = SystemCoreClock;
    uint32_t baudRate;

    while( sysClkTmp > hz )
    {
        divisor++;
        sysClkTmp = ( sysClkTmp >> 1 );

        if( divisor >= 7 )
        {
            break;
        }
    }

    baudRate =( ( ( divisor & 0x4 ) == 0 ) ? 0x0 : SPI_BR_PRESCALER_32 ) |
              ( ( ( divisor & 0x2 ) == 0 ) ? 0x0 : SPI_BR_PRESCALER_8 ) |
              ( ( ( divisor & 0x1 ) == 0 ) ? 0x0 : SPI_BR_PRESCALER_4 );

    SpiHandle[obj->SpiId].Init.BaudRatePres = baudRate;
}

uint16_t SpiInOut( Spi_t *obj, uint16_t outData )
{
    uint8_t rxData = 0;
    SPI_Module *spi = SpiHandle[obj->SpiId].SPI;

    if( ( obj == NULL ) || ( spi == NULL ) )
    {
        assert_param( LMN_STATUS_ERROR );
    }

//    SPI_Enable(spi, ENABLE);

    CRITICAL_SECTION_BEGIN( );

    while( SPI_I2S_GetStatus( spi, SPI_I2S_TE_FLAG ) == RESET );
    spi->DAT = ( uint16_t ) ( outData & 0xFF );

    while( SPI_I2S_GetStatus( spi, SPI_I2S_RNE_FLAG ) == RESET );
    rxData = ( uint16_t ) spi->DAT;

    CRITICAL_SECTION_END( );

    return( rxData );
}

